﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Documents;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Xml;
using SHomeWorkshop.LunarTools;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using SHomeWorkshop.LunarMind.Commands;

namespace SHomeWorkshop.LunarMind
{
    public class Relation : AdornerDecorator, INotifyPropertyChanged
    {
        public Relation(MainWindow editor, Title startTitle, Title endTitle)
        {
            this.mainTextBlock.MouseRightButtonUp += Path_MouseRightButtonUp;

            Canvas.SetZIndex(this, 1000);

            this.editor = editor;
            this.startTitle = startTitle;
            this.endTitle = endTitle;

            this.startTitleID = startTitle.ID;
            this.endTitleID = endTitle.ID;

            this.RebuildANewID();

            this.baseCanvas.ClipToBounds = false;
            //将此Canvas的Width和Hdight都设置为０，然后将此属性设置为false，则应显示其中的TextBlock。

            this.Child = baseCanvas;
            this.baseCanvas.Children.Add(mainPath);
            this.baseCanvas.Children.Add(startArrowPath);
            this.baseCanvas.Children.Add(endArrowPath);
            this.baseCanvas.Children.Add(mainTextBlock);

            mainPath.Cursor = Cursors.Hand;
            mainPath.Stroke = lineColor;
            mainPath.StrokeThickness = lineWidth;
            mainPath.StrokeDashArray = LineDashType.dashDotCollection;

            PathGeometry mainPg = new PathGeometry();
            this.mainPath.Data = mainPg;

            mainPathFigure.IsClosed = false;

            mainPg.Figures.Add(mainPathFigure);
            mainPathFigure.Segments.Add(mainBezierSegment);

            #region 两个箭头。
            startArrowPath.Cursor = Cursors.Hand;
            startArrowPath.Fill = startArrowPath.Stroke = lineColor;
            startArrowPath.StrokeThickness = lineWidth;
            PathGeometry startArrowPg = new PathGeometry();
            startArrowPath.Data = startArrowPg;

            startArrowPathFigure.IsClosed = true;
            startArrowPathFigure.IsFilled = true;
            startArrowPathFigure.Segments.Add(startArrowPolyLineSegment);

            startArrowPg.Figures.Add(startArrowPathFigure);

            endArrowPath.Cursor = Cursors.Hand;
            endArrowPath.Fill = endArrowPath.Stroke = lineColor;
            endArrowPath.StrokeThickness = lineWidth;
            PathGeometry endArrowPg = new PathGeometry();
            endArrowPath.Data = endArrowPg;

            endArrowPathFigure.IsClosed = true;
            endArrowPathFigure.IsFilled = true;
            endArrowPathFigure.Segments.Add(endArrowPolyLineSegment);

            endArrowPg.Figures.Add(endArrowPathFigure);

            #endregion

            this.mainPath.ToolTip = this.startArrowPath.ToolTip = this.endArrowPath.ToolTip = "编辑文本：Ctrl+Enter";
            this.mainTextBlock.ToolTip = "编辑文本：Ctrl+Enter（或双击）";

            this.mainPath.MouseDown += new System.Windows.Input.MouseButtonEventHandler(Path_MouseDown);
            this.startArrowPath.MouseDown += new System.Windows.Input.MouseButtonEventHandler(Path_MouseDown);
            this.endArrowPath.MouseDown += new System.Windows.Input.MouseButtonEventHandler(Path_MouseDown);
            this.mainTextBlock.MouseDown += new System.Windows.Input.MouseButtonEventHandler(mainTextBlock_MouseDown);

            this.mainPath.MouseRightButtonUp += new MouseButtonEventHandler(Path_MouseRightButtonUp);
            this.startArrowPath.MouseRightButtonUp += new MouseButtonEventHandler(Path_MouseRightButtonUp);
            this.endArrowPath.MouseRightButtonUp += new MouseButtonEventHandler(Path_MouseRightButtonUp);
        }

        private static ContextMenu s_ContextMenu;

        public static ContextMenu S_ContextMenu
        {
            get { return Relation.s_ContextMenu; }
        }

        private static MenuItem miDot;
        private static MenuItem miDash;
        private static MenuItem miDashDot;
        private static MenuItem miDashDotDot;
        private static MenuItem miSolid;

        private static MenuItem miStartArrow;
        private static MenuItem miEndArrow;
        private static MenuItem miDoubleArrow;
        private static MenuItem miNoneArrow;

        private static MenuItem miResetRelationLocation;

        public static void ShowContextMenu()
        {
            if (s_ContextMenu != null) s_ContextMenu.IsOpen = true;
        }

        static Relation()
        {
            s_ContextMenu = new ContextMenu();
            s_ContextMenu.Opened += new RoutedEventHandler(s_ContextMenu_Opened);

            miDot = new MenuItem() { Header = "点线(_T)", Height = 20 };
            miDash = new MenuItem() { Header = "划线(_A)", Height = 20 };
            miDashDot = new MenuItem() { Header = "点划线(_H)", Height = 20 };
            miDashDotDot = new MenuItem() { Header = "点点划线(_D)", Height = 20 };
            miSolid = new MenuItem() { Header = "实线(_S)", Height = 20 };

            miStartArrow = new MenuItem() { Header = "首箭头(_S)", Height = 20 };
            miEndArrow = new MenuItem() { Header = "尾箭头(_E)", Height = 20 };
            miDoubleArrow = new MenuItem() { Header = "双箭头(_L)", Height = 20 };
            miNoneArrow = new MenuItem() { Header = "无箭头(_N)", Height = 20 };

            miResetRelationLocation = new MenuItem() { Header = "重置位置(_L)", Height = 20 };

            miDot.Click += new RoutedEventHandler(miDot_Click);
            miDash.Click += new RoutedEventHandler(miDash_Click);
            miDashDot.Click += new RoutedEventHandler(miDashDot_Click);
            miDashDotDot.Click += new RoutedEventHandler(miDashDotDot_Click);
            miSolid.Click += new RoutedEventHandler(miSolid_Click);

            miStartArrow.Click += new RoutedEventHandler(miStartArrow_Click);
            miEndArrow.Click += new RoutedEventHandler(miEndArrow_Click);
            miDoubleArrow.Click += new RoutedEventHandler(miDoubleArrow_Click);
            miNoneArrow.Click += new RoutedEventHandler(miNoneArrow_Click);

            miResetRelationLocation.Click += new RoutedEventHandler(miResetRelationLocation_Click);

            s_ContextMenu.Items.Add(miDot);
            s_ContextMenu.Items.Add(miDash);
            s_ContextMenu.Items.Add(miDashDot);
            s_ContextMenu.Items.Add(miDashDotDot);
            s_ContextMenu.Items.Add(miSolid);
            s_ContextMenu.Items.Add(new Separator());
            s_ContextMenu.Items.Add(miStartArrow);
            s_ContextMenu.Items.Add(miEndArrow);
            s_ContextMenu.Items.Add(miDoubleArrow);
            s_ContextMenu.Items.Add(miNoneArrow);
            s_ContextMenu.Items.Add(new Separator());
            s_ContextMenu.Items.Add(miResetRelationLocation);
        }

        static void miResetRelationLocation_Click(object sender, RoutedEventArgs e)
        {
            Relation r = Globals.MainWindow.SelectedRelation;

            if (r == null) return;

            ModifingItem mi = new ModifingItem()
            {
                IsNeedRefreshTitleAndLinesLocation = false,
                ModifingName = "重置关系线位置"
            };

            string oldStartCtrlText = r.StartCtrlPoint.ToString();
            string oldEndCtrlText = r.EndCtrlPoint.ToString();

            AddRelationCommand.ResetRelationLocation(r, r.StartTitle, r.EndTitle);

            Action actStartCtrl = new Action(r.id, -1, r.GetType().Name, "StartCtrlPoint",
                oldStartCtrlText, r.StartCtrlPoint.ToString());

            Action actEndCtrl = new Action(r.id, -1, r.GetType().Name, "EndCtrlPoint",
                oldEndCtrlText, r.EndCtrlPoint.ToString());

            mi.Add(actStartCtrl);
            mi.Add(actEndCtrl);

            Globals.MainWindow.Do(mi);
            Globals.MainWindow.IsModified = true;

            Globals.MainWindow.SelectedRelation = r;//这是为了刷新控制点的位置。
        }

        static void s_ContextMenu_Opened(object sender, RoutedEventArgs e)
        {
            if (Globals.MainWindow.SelectedRelation == null) return;

            Image imgArrow = new Image();
            imgArrow.Width = imgArrow.Height = 16;
            imgArrow.Source = new BitmapImage(new Uri("pack://Application:,,,/LunarMind;component/Resources/Icons/Selected.png"));

            switch (Globals.MainWindow.SelectedRelation.Arrows)
            {
                case ArrowType.All:
                    {
                        miStartArrow.Icon = null;
                        miEndArrow.Icon = null;

                        miDoubleArrow.Icon = imgArrow;
                        miNoneArrow.Icon = null;
                        break;
                    }
                case ArrowType.Start:
                    {
                        miDoubleArrow.Icon = null;
                        miEndArrow.Icon = null;

                        miStartArrow.Icon = imgArrow;
                        miNoneArrow.Icon = null;
                        break;
                    }
                case ArrowType.End:
                    {
                        miDoubleArrow.Icon = null;
                        miStartArrow.Icon = null;

                        miEndArrow.Icon = imgArrow;
                        miNoneArrow.Icon = null;
                        break;
                    }
                default://无箭头
                    {
                        miDoubleArrow.Icon = null;
                        miStartArrow.Icon = null;

                        miEndArrow.Icon = null;
                        miNoneArrow.Icon = imgArrow;
                        break;
                    }
            }

            Image imgLine = new Image();
            imgLine.Width = imgArrow.Height = 16;
            imgLine.Source = new BitmapImage(new Uri("pack://Application:,,,/LunarMind;component/Resources/Icons/Selected.png"));

            switch (Globals.MainWindow.SelectedRelation.LineDash)
            {
                case LineDashType.DashType.Dash:
                    {
                        miDot.Icon = null;
                        miDashDot.Icon = null;
                        miDashDotDot.Icon = null;
                        miSolid.Icon = null;

                        miDash.Icon = imgLine;
                        break;
                    }
                case LineDashType.DashType.DashDot:
                    {
                        miDot.Icon = null;
                        miDash.Icon = null;
                        miDashDotDot.Icon = null;
                        miSolid.Icon = null;

                        miDashDot.Icon = imgLine;
                        break;
                    }
                case LineDashType.DashType.DashDotDot:
                    {
                        miDot.Icon = null;
                        miDash.Icon = null;
                        miDashDot.Icon = null;
                        miSolid.Icon = null;

                        miDashDotDot.Icon = imgLine;
                        break;
                    }
                case LineDashType.DashType.Dot:
                    {
                        miDash.Icon = null;
                        miDashDot.Icon = null;
                        miDashDotDot.Icon = null;
                        miSolid.Icon = null;

                        miDot.Icon = imgLine;
                        break;
                    }
                case LineDashType.DashType.Solid:
                    {
                        miDot.Icon = null;
                        miDash.Icon = null;
                        miDashDot.Icon = null;
                        miDashDotDot.Icon = null;

                        miSolid.Icon = imgLine;
                        break;
                    }
            }
        }

        static void miDoubleArrow_Click(object sender, RoutedEventArgs e)
        {
            SetArrows(ArrowType.All);
        }

        static void miNoneArrow_Click(object sender, RoutedEventArgs e)
        {
            SetArrows(ArrowType.None);
        }

        static void miEndArrow_Click(object sender, RoutedEventArgs e)
        {
            SetArrows(ArrowType.End);
        }

        static void miStartArrow_Click(object sender, RoutedEventArgs e)
        {
            SetArrows(ArrowType.Start);
        }

        private static void SetArrows(ArrowType newArrowType)
        {
            if (Globals.MainWindow.SelectedRelation != null)
            {
                Relation r = Globals.MainWindow.SelectedRelation;
                if (r.Arrows != newArrowType)
                {
                    ModifingItem mi = new ModifingItem() { ModifingName = "设置关系线箭头指向" };
                    Action actArrows = new Action(r.id, -1, r.GetType().Name, "Arrows",
                        r.Arrows.ToString(), newArrowType.ToString());

                    r.Arrows = newArrowType;

                    mi.Add(actArrows);

                    Globals.MainWindow.Do(mi);
                    Globals.MainWindow.IsModified = true;
                }
            }
        }

        static void miSolid_Click(object sender, RoutedEventArgs e)
        {
            SetLineDash(LineDashType.DashType.Solid);
        }

        static void miDashDotDot_Click(object sender, RoutedEventArgs e)
        {
            SetLineDash(LineDashType.DashType.DashDotDot);
        }

        static void miDashDot_Click(object sender, RoutedEventArgs e)
        {
            SetLineDash(LineDashType.DashType.DashDot);
        }

        static void miDash_Click(object sender, RoutedEventArgs e)
        {
            SetLineDash(LineDashType.DashType.Dash);
        }

        static void miDot_Click(object sender, RoutedEventArgs e)
        {
            SetLineDash(LineDashType.DashType.Dot);
        }

        private static void SetLineDash(LineDashType.DashType newDashType)
        {
            if (Globals.MainWindow.SelectedRelation != null)
            {
                Relation r = Globals.MainWindow.SelectedRelation;
                if (r.LineDash != newDashType)
                {
                    ModifingItem mi = new ModifingItem() { ModifingName = "设置关系线线型" };
                    Action actDashType = new Action(r.id, -1, r.GetType().Name, "LineDash",
                        r.LineDash.ToString(), newDashType.ToString());

                    r.LineDash = newDashType;

                    mi.Add(actDashType);
                    Globals.MainWindow.Do(mi);
                    Globals.MainWindow.IsModified = true;
                }
            }
        }

        void Path_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            s_ContextMenu.IsOpen = true;
        }

        void mainTextBlock_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (e.ClickCount == 2)
            {
                if (Globals.MainWindow.SelectedRelation != this)
                {
                    Globals.MainWindow.SelectedRelation = this;
                }

                Commands.EditCommentTextCommand.EditRelationText();

                e.Handled = true;
                return;
            }

            editor.SelectedRelation = this; e.Handled = true;
        }

        void Path_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            editor.SelectedRelation = this; e.Handled = true;
        }

        private string id;

        public string ID
        {
            get { return id; }
            set
            {
                id = value;
                this.xmlData.SetAttribute("ID", value);
            }
        }

        public void RebuildANewID()
        {
            id = Guid.NewGuid().ToString();
            if (this.xmlData != null)
            {
                this.xmlData.SetAttribute("ID", id);
            }
        }

        public void BuildRelation()
        {
            if (this.xmlData == null) return;

            XmlAttribute attrID = this.xmlData.GetAttribute("ID");
            if (attrID != null)
            {
                this.id = attrID.Value;
            }

            XmlAttribute attrStartTitleID = this.xmlData.GetAttribute("StartTitleID");
            if (attrStartTitleID != null && attrStartTitleID.Value.Length > 0)
            {
                this.startTitleID = attrStartTitleID.Value;
                this.SearchStartTitle();
            }

            XmlAttribute attrEndTitleID = this.xmlData.GetAttribute("EndTitleID");
            if (attrEndTitleID != null && attrEndTitleID.Value.Length > 0)
            {
                this.endTitleID = attrEndTitleID.Value;
                this.SearchEndTitle();
            }

            XmlAttribute attrLineColor = this.xmlData.GetAttribute("LineColor");
            if (attrLineColor != null)
            {
                this.lineColor = ColorPicker.GetBrushByName(attrLineColor.Value);
                this.mainPath.Stroke = this.lineColor;
                this.startArrowPath.Fill = this.startArrowPath.Stroke = this.lineColor;
                this.endArrowPath.Fill = this.endArrowPath.Stroke = this.lineColor;
            }

            XmlAttribute attrLineWidth = this.xmlData.GetAttribute("LineWidth");
            if (attrLineWidth != null)
            {
                this.lineWidth = double.Parse(attrLineWidth.Value);
                this.mainPath.StrokeThickness = this.lineWidth;
            }

            XmlAttribute attrComment = this.xmlData.GetAttribute("CommentText");
            if (attrComment != null)
            {
                this.commentText = attrComment.Value;
                RefreshCommentText();
            }

            XmlAttribute attrStartCtrl = this.xmlData.GetAttribute("StartCtrlPoint");
            if (attrStartCtrl != null)
            {
                startCtrlPoint = Point.Parse(attrStartCtrl.Value);
            }

            XmlAttribute attrEndCtrl = this.xmlData.GetAttribute("EndCtrlPoint");
            if (attrEndCtrl != null)
            {
                endCtrlPoint = Point.Parse(attrEndCtrl.Value);
            }

            XmlAttribute attrLineDash = this.xmlData.GetAttribute("LineDash");
            if (attrLineDash != null)
            {
                lineDash = (LineDashType.DashType)Enum.Parse(typeof(LineDashType.DashType), attrLineDash.Value);
                RefreshDashType();
            }

            XmlAttribute attrTextForeground = this.xmlData.GetAttribute("TextForeground");
            if (attrTextForeground != null)
            {
                this.textForeground = ColorPicker.GetBrushByName(attrTextForeground.Value);
                this.mainTextBlock.Foreground = this.textForeground;
            }

            RefreshTextBackground();

            XmlAttribute attrArrows = this.xmlData.GetAttribute("Arrows");
            if (attrArrows != null)
            {
                this.arrows = (ArrowType)Enum.Parse(typeof(ArrowType), attrArrows.Value);
            }

            this.RefreshArrows();//为保证不出问题，不管有没有读取到，都刷新一下。

            XmlAttribute attrRotateAngle = this.xmlData.GetAttribute("RotateAngle");
            if (attrRotateAngle != null)
            {
                this.rotateAngle = double.Parse(attrRotateAngle.Value);
                RefreshRotateAngle();
            }

            RefreshLocation();
        }

        public void RefreshTextBackground()
        {
            XmlAttribute attrTextBackground = this.xmlData.GetAttribute("TextBackground");
            if (attrTextBackground != null)
            {
                this.textBackground = ColorPicker.GetBrushByName(attrTextBackground.Value);
                this.mainTextBlock.Background = this.textBackground;
            }
            else
            {
                if (this.editor != null)
                {
                    this.textBackground = this.editor.mainCanvas.Background;
                    this.mainTextBlock.Background = this.textBackground;
                }
            }
        }

        private Point outerStartPoint;
        public Point OuterStartPoint { get { return outerStartPoint; } }

        private Point outerEndPoint;
        public Point OuterEndPoint { get { return outerEndPoint; } }

        private Point outerStartControlPoint;
        public Point OuterStartControlPoint { get { return OuterStartControlPoint; } }

        private Point outerEndControlPoint;
        public Point OuterEndControlPoint { get { return OuterEndControlPoint; } }

        //x = Math.pow(1-0.5, 2)*P0.x + 2*0.5*(1-0.5)*P1.x + Math.pow(0.5, 2)*P2.x
        //y = Math.pow(1-0.5, 2)*P0.y + 2*0.5*(1-0.5)*P1.y + Math.pow(0.5, 2)*P2.y

        //P0起点；P1控制点；P2终点。
        //三次方贝塞尔曲线
        //P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝塞尔曲线。曲线起始于P0走向P1，
        //并从P2的方向来到P3。一般不会经过P1或P2；
        //这两个点只是在那里提供方向资讯。
        //P0和P1之间的间距，决定了曲线在转而趋进P3之前，走向P2方向的“长度有多长”。
        //曲线的参数形式为：
        //B(t)=P0(1-t)^3+3*P1*t*(1-t)^2+3*P2*t^2*(1-t)+P3*t^3,t∈[0,1]

        public void RefreshLocation(Rect? tmpStartRect = null, Rect? tmpEndRect = null)
        {
            if (StartTitle.BeCollapsed || EndTitle.BeCollapsed)
            {
                this.Visibility = Visibility.Collapsed; return;
            }
            else
            {
                this.Visibility = Visibility.Visible;
            }

            //先根据两个控制点求出两个端点

            Rect startRect;
            if (tmpStartRect == null)
            {
                startRect = StartRect;
            }
            else
            {
                startRect = (Rect)tmpStartRect;
            }

            Rect endRect;
            if (tmpEndRect == null)
            {
                endRect = EndRect;
            }
            else
            {
                endRect = (Rect)tmpEndRect;
            }


            Point startCenter = StartCenter; Point endCenter = EndCenter;

            Point outStartCtrl = new Point(startCenter.X + startCtrlPoint.X, startCenter.Y + startCtrlPoint.Y);
            Point outEndCtrl = new Point(endCenter.X + endCtrlPoint.X, endCenter.Y + endCtrlPoint.Y);

            outerStartControlPoint = outStartCtrl;
            outerEndControlPoint = outEndCtrl;

            PointToRect.ArrowPoints aptStart = PointToRect.GetCrossPointToRect(startRect, outStartCtrl);
            PointToRect.ArrowPoints aptEnd = PointToRect.GetCrossPointToRect(endRect, outEndCtrl);

            outerStartPoint = aptStart.Top;
            outerEndPoint = aptEnd.Top;

            double t = 0.5;

            //Math.Pow(1 - t, 3) * P0.X + 3 * P1.X * t * Math.Pow(1 - t, 2) + 3 * P2.X * Math.Pow(t, 2) * (1 - t) + P3.X * Math.Pow(0.5, 3)

            centerPoint = new Point(
                Math.Pow(1 - t, 3) * aptStart.Top.X +
                3 * outStartCtrl.X * t * Math.Pow(1 - t, 2) +
                3 * outEndCtrl.X * Math.Pow(t, 2) * (1 - t) + aptEnd.Top.X * Math.Pow(0.5, 3),
                Math.Pow(1 - t, 3) * aptStart.Top.Y +
                3 * outStartCtrl.Y * t * Math.Pow(1 - t, 2) +
                3 * outEndCtrl.Y * Math.Pow(t, 2) * (1 - t) + aptEnd.Top.Y * Math.Pow(0.5, 3));

            //传进来的startCtrlPoint和endCtrlPoint是相对值。

            Point actualStart = new Point(aptStart.Top.X - centerPoint.X, aptStart.Top.Y - centerPoint.Y);
            Point actualEnd = new Point(aptEnd.Top.X - centerPoint.X, aptEnd.Top.Y - centerPoint.Y);

            Point actualStartCenter = new Point(startCenter.X - centerPoint.X, startCenter.Y - centerPoint.Y);
            Point actualEndCenter = new Point(endCenter.X - centerPoint.X, endCenter.Y - centerPoint.Y);

            Point actualStartCtrl = new Point(actualStartCenter.X + startCtrlPoint.X, actualStartCenter.Y + startCtrlPoint.Y);
            Point actualEndCtrl = new Point(actualEndCenter.X + endCtrlPoint.X, actualEndCenter.Y + endCtrlPoint.Y);

            Canvas.SetLeft(this, centerPoint.X);
            Canvas.SetTop(this, centerPoint.Y);

            this.mainTextBlock.InvalidateArrange();
            this.mainTextBlock.UpdateLayout();

            Canvas.SetLeft(this.mainTextBlock, -(this.mainTextBlock.ActualWidth / 2));
            Canvas.SetTop(this.mainTextBlock, -(this.mainTextBlock.ActualHeight / 2));

            this.mainPathFigure.StartPoint = actualStart;
            this.mainBezierSegment.Point1 = actualStartCtrl;
            this.mainBezierSegment.Point2 = actualEndCtrl;
            this.mainBezierSegment.Point3 = actualEnd;

            Point actualStartArrowPtA = new Point(aptStart.Start.X - centerPoint.X, aptStart.Start.Y - centerPoint.Y);
            Point actualStartArrowPtB = new Point(aptStart.End.X - centerPoint.X, aptStart.End.Y - centerPoint.Y);

            Point actualEndArrowPtA = new Point(aptEnd.Start.X - centerPoint.X, aptEnd.Start.Y - centerPoint.Y);
            Point actualEndArrowPtB = new Point(aptEnd.End.X - centerPoint.X, aptEnd.End.Y - centerPoint.Y);

            startArrowPathFigure.StartPoint = actualStart;
            startArrowPolyLineSegment.Points = new PointCollection(){
                actualStartArrowPtA, actualStartArrowPtB,
            };

            endArrowPathFigure.StartPoint = actualEnd;
            endArrowPolyLineSegment.Points = new PointCollection(){
                actualEndArrowPtA,actualEndArrowPtB,
            };
        }

        private Canvas baseCanvas = new Canvas() { Width = 0, Height = 0 };

        private Path mainPath = new Path();

        private PathFigure mainPathFigure = new PathFigure();

        private Path startArrowPath = new Path() { Visibility = Visibility.Hidden };//默认情况下不显示。

        private Path endArrowPath = new Path();

        private PathFigure startArrowPathFigure = new PathFigure();

        private PathFigure endArrowPathFigure = new PathFigure();

        private PolyLineSegment startArrowPolyLineSegment = new PolyLineSegment();

        private PolyLineSegment endArrowPolyLineSegment = new PolyLineSegment();

        private BezierSegment mainBezierSegment = new BezierSegment();

        private TextBlock mainTextBlock = new TextBlock() { Background = Brushes.White, Visibility = Visibility.Hidden };

        public Point StartCenter
        {
            get
            {
                return new Point(Canvas.GetLeft(startTitle) + startTitle.ActualWidth / 2,
                    Canvas.GetTop(startTitle) + startTitle.ActualHeight / 2);
            }
        }

        public Point EndCenter
        {
            get
            {
                return new Point(Canvas.GetLeft(endTitle) + endTitle.ActualWidth / 2,
                    Canvas.GetTop(endTitle) + endTitle.ActualHeight / 2);
            }
        }

        public Rect StartRect
        {
            get
            {
                double indentWidth = (startTitle.Direction == TitleDirection.Right) ? startTitle.IndentWidth : -startTitle.IndentWidth;

                return new Rect(startTitle.LeftTop.X + startTitle.MainBorder.Margin.Left + indentWidth,
                    startTitle.LeftTop.Y + startTitle.MainBorder.Margin.Top,
                    startTitle.ActualWidth - startTitle.MainBorder.Margin.Left - startTitle.MainBorder.Margin.Right,
                    startTitle.ActualHeight - startTitle.MainBorder.Margin.Top - startTitle.MainBorder.Margin.Bottom);
            }
        }

        public Rect EndRect
        {
            get
            {
                double indentWidth = (endTitle.Direction == TitleDirection.Right) ? endTitle.IndentWidth : -endTitle.IndentWidth;

                return new Rect(endTitle.LeftTop.X + endTitle.MainBorder.Margin.Left + indentWidth,
                    endTitle.LeftTop.Y + endTitle.MainBorder.Margin.Top,
                    endTitle.ActualWidth - endTitle.MainBorder.Margin.Left - endTitle.MainBorder.Margin.Right,
                    endTitle.ActualHeight - endTitle.MainBorder.Margin.Top - endTitle.MainBorder.Margin.Bottom);
            }
        }

        private XmlNode xmlData;

        public XmlNode XmlData
        {
            get { return xmlData; }
            set
            {
                xmlData = value;
                BuildRelation();
            }
        }

        private MainWindow editor;

        public MainWindow Editor
        {
            get { return editor; }
        }

        private Title startTitle;

        public Title StartTitle
        {
            get { return startTitle; }
        }

        private void SearchStartTitle()
        {
            if (this.editor == null || this.startTitleID == null || this.startTitleID.Length <= 0)
            {
                this.startTitle = null;
                return;
            }

            foreach (UIElement ue in editor.mainCanvas.Children)
            {
                Title t = ue as Title;
                if (t == null) continue;

                if (t.ID == this.startTitleID)
                {
                    this.startTitle = t;
                    return;
                }
            }
        }

        private Title endTitle;

        public Title EndTitle
        {
            get { return endTitle; }
        }

        private void SearchEndTitle()
        {
            if (this.editor == null || this.endTitleID == null || this.endTitleID.Length <= 0)
            {
                this.endTitle = null;
                return;
            }

            foreach (UIElement ue in editor.mainCanvas.Children)
            {
                Title t = ue as Title;
                if (t == null) continue;

                if (t.ID == this.endTitleID)
                {
                    this.endTitle = t;
                    return;
                }
            }
        }

        private string startTitleID;

        public string StartTitleID
        {
            get { return startTitleID; }
            set
            {
                startTitleID = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("StartTitleID", value);
                }
                SearchStartTitle();
            }
        }

        private string endTitleID;

        public string EndTitleID
        {
            get { return endTitleID; }
            set
            {
                endTitleID = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("EndTitleID", value);
                }
                SearchEndTitle();
            }
        }

        private double rotateAngle = 0;

        public double RotateAngle
        {
            get { return rotateAngle; }
            set
            {
                if (value > 180)
                {
                    rotateAngle = 180;
                }
                else if (value < -180)
                {
                    rotateAngle = -180;
                }
                else
                {
                    rotateAngle = value;
                }

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("RotateAngle", rotateAngle.ToString());
                }

                RefreshRotateAngle();
            }
        }

        private static readonly Point defRenderCenter = new Point(0.5, 0.5);
        private static readonly RotateTransform defRotateTransform = new RotateTransform(0);

        private void RefreshRotateAngle()
        {
            if (this.mainTextBlock.RenderTransformOrigin != defRenderCenter)
            {
                this.mainTextBlock.RenderTransformOrigin = defRenderCenter;
            }

            if (rotateAngle == 0)
            {
                this.mainTextBlock.RenderTransform = defRotateTransform;
            }
            else
            {
                this.mainTextBlock.RenderTransform = new RotateTransform(rotateAngle);
            }
        }

        public void RefreshTbxCaret()
        {
            if (editor == null) return;

            if (editor.mainCanvas.Children.Contains(editor.tbxCaret) == false)
            {
                editor.mainCanvas.Children.Add(editor.tbxCaret);
                Canvas.SetZIndex(editor.tbxCaret, 0);
            }

            editor.tbxCaret.Width = this.mainTextBlock.Width;
            editor.tbxCaret.Height = this.mainTextBlock.Height;

            Canvas.SetLeft(editor.tbxCaret, Canvas.GetLeft(this));
            Canvas.SetTop(editor.tbxCaret, Canvas.GetTop(this));

            editor.tbxCaret.InvalidateArrange();
            editor.tbxCaret.UpdateLayout();

            editor.tbxCaret.Focus();
            editor.tbxCaret.Select(0, 0);
        }

        #region INotifyPropertyChanged 的实现

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(object sender,
           PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(sender, e);
            }
        }

        #endregion

        /// <summary>
        /// 一次公式级的撤销操作。
        /// </summary>
        /// <param name="action">一次撤销或重做可以涉及到多个公式（因而包含多次公式级的撤销或重做）。</param>
        public virtual void Undo(Action action)
        {
            if (action != null)
            {
                SetProperty(action.PropertyOrActionTypeName, action.OldValue);
            }
        }

        private Brush lineColor = Brushes.DarkCyan;

        public Brush LineColor
        {
            get { return lineColor; }
            set
            {
                lineColor = value;
                this.mainPath.Stroke = lineColor;
                this.startArrowPath.Fill = this.startArrowPath.Stroke = lineColor;
                this.endArrowPath.Fill = this.endArrowPath.Stroke = lineColor;

                this.xmlData.SetAttribute("LineColor", ColorPicker.GetBrushName(lineColor));
            }
        }

        private double lineWidth = 2;

        public double LineWidth
        {
            get { return lineWidth; }
            set
            {
                lineWidth = value;
                this.mainPath.StrokeThickness = lineWidth;
                this.xmlData.SetAttribute("LineWidth", lineWidth.ToString());
            }
        }

        public enum ArrowType { Start, End, All, None };

        private ArrowType arrows = ArrowType.End;

        /// <summary>
        /// 默认值是尾箭头。这是考虑到用户的使用习惯。
        /// </summary>
        public ArrowType Arrows
        {
            get { return arrows; }
            set
            {
                arrows = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("Arrows", value.ToString());
                }

                RefreshArrows();
            }
        }

        private void RefreshArrows()
        {
            switch (arrows)
            {
                case ArrowType.All:
                    {
                        startArrowPath.Visibility = Visibility.Visible;
                        endArrowPath.Visibility = Visibility.Visible;
                        break;
                    }
                case ArrowType.Start:
                    {
                        startArrowPath.Visibility = Visibility.Visible;
                        endArrowPath.Visibility = Visibility.Hidden;
                        break;
                    }
                case ArrowType.End:
                    {
                        startArrowPath.Visibility = Visibility.Hidden;
                        endArrowPath.Visibility = Visibility.Visible;
                        break;
                    }
                default://无箭头
                    {
                        startArrowPath.Visibility = Visibility.Hidden;
                        endArrowPath.Visibility = Visibility.Hidden;
                        break;
                    }
            }
        }

        private Brush textForeground = Brushes.DimGray;

        /// <summary>
        /// 备注文本的前景色。
        /// </summary>
        public Brush TextForeground
        {
            get { return textForeground; }
            set
            {
                textForeground = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("TextForeground", ColorPicker.GetBrushName(value));
                }

                this.mainTextBlock.Foreground = value;
            }
        }

        private Brush textBackground = Brushes.Transparent;

        /// <summary>
        /// 备注文本的背景色。
        /// </summary>
        public Brush TextBackground
        {
            get { return textBackground; }
            set
            {
                textBackground = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("TextBackground", ColorPicker.GetBrushName(value));
                }

                this.mainTextBlock.Background = value;
            }
        }

        private LineDashType.DashType lineDash = LineDashType.DashType.DashDot;

        public LineDashType.DashType LineDash
        {
            get { return lineDash; }
            set
            {
                lineDash = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("LineDash", lineDash.ToString());
                }

                RefreshDashType();
            }
        }

        private void RefreshDashType()
        {
            switch (lineDash)
            {
                case LineDashType.DashType.Dash:
                    {
                        this.mainPath.StrokeDashArray = LineDashType.dashCollection; break;
                    }
                case LineDashType.DashType.DashDotDot:
                    {
                        this.mainPath.StrokeDashArray = LineDashType.dashDotDotCollection; break;
                    }
                case LineDashType.DashType.Dot:
                    {
                        this.mainPath.StrokeDashArray = LineDashType.dotCollection; break;
                    }
                case LineDashType.DashType.Solid:
                    {
                        this.mainPath.StrokeDashArray = LineDashType.solidCollection; break;
                    }
                default:
                    {
                        this.mainPath.StrokeDashArray = LineDashType.dashDotCollection; break;
                    }
            }
        }

        private Point startCtrlPoint;

        public Point StartCtrlPoint
        {
            get { return startCtrlPoint; }
            set
            {
                startCtrlPoint = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute("StartCtrlPoint", value.ToString());
                }
                RefreshLocation();
            }
        }

        /// <summary>
        /// 此方法不会修改后台Xml数据。
        /// </summary>
        /// <param name="newTempPoint"></param>
        public void SetStartCtrlPointWhenMouseMove(Point newTempPoint)
        {
            startCtrlPoint = newTempPoint;
            RefreshLocation();
        }

        public void SetStartCtrlPoint(Point newStartPoint)
        {
            this.startCtrlPoint = newStartPoint;
        }

        private Point endCtrlPoint;

        public Point EndCtrlPoint
        {
            get { return endCtrlPoint; }
            set
            {
                endCtrlPoint = value;
                this.xmlData.SetAttribute("EndCtrlPoint", value.ToString());
                RefreshLocation();
            }
        }

        public void SetEndCtrlPointWhenMouseMove(Point newTempPoint)
        {
            endCtrlPoint = newTempPoint;
            RefreshLocation();
        }

        public void SetEndCtrlPoint(Point newEndPoint)
        {
            this.endCtrlPoint = newEndPoint;
        }

        private Point centerPoint;

        public Point CenterPoint
        {
            get { return centerPoint; }
        }

        private string commentText;

        public string CommentText
        {
            get { return commentText; }
            set
            {
                commentText = value;
                this.xmlData.SetAttribute("CommentText", value);

                RefreshCommentText();
            }
        }

        private void RefreshCommentText()
        {
            if (commentText == null || commentText.Length <= 0)
            {
                this.mainTextBlock.Text = "";
                mainTextBlock.Visibility = Visibility.Hidden;
            }
            else
            {
                this.mainTextBlock.Text = commentText;
                mainTextBlock.Visibility = Visibility.Visible;
            }
        }

        /// <summary>
        /// 一次公式级的重做操作。
        /// </summary>
        /// <param name="action"></param>
        public virtual void Redo(Action action)
        {
            if (action != null)
            {
                SetProperty(action.PropertyOrActionTypeName, action.NewValue);
            }
        }

        public virtual void SetProperty(string propertyName, string value)
        {
            switch (propertyName)
            {
                case "LineColor":
                    {
                        this.LineColor = ColorPicker.GetBrushByName(value);
                        break;
                    }
                case "LineWidth":
                    {
                        this.LineWidth = double.Parse(value);
                        break;
                    }
                case "LineDash":
                    {
                        this.LineDash = (LineDashType.DashType)Enum.Parse(typeof(LineDashType.DashType), value);
                        break;
                    }
                case "CommentText":
                    {
                        this.CommentText = value;
                        break;
                    }
                case "StartTitleID":
                    {
                        this.StartTitleID = value;
                        break;
                    }
                case "EndTitleID":
                    {
                        this.EndTitleID = value;
                        break;
                    }
                case "StartCtrlPoint":
                    {
                        this.StartCtrlPoint = Point.Parse(value);
                        break;
                    }
                case "EndCtrlPoint":
                    {
                        this.EndCtrlPoint = Point.Parse(value);
                        break;
                    }
                case "TextForeground":
                    {
                        this.TextForeground = ColorPicker.GetBrushByName(value);
                        break;
                    }
                case "TextBackground":
                    {
                        this.TextBackground = ColorPicker.GetBrushByName(value);
                        break;
                    }
                case "Arrows":
                    {
                        this.Arrows = (ArrowType)Enum.Parse(typeof(ArrowType), value);
                        break;
                    }
                case "RotateAngle":
                    {
                        this.RotateAngle = double.Parse(value); break;
                    }
            }
        }
    }
}
